{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "随机点坐标：(0.5650174365385676, -0.24035791682059982)\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "import math\n",
    "\n",
    "def generate_random_point(cx, cy, num, num_points=1000, min_distance=0.1):\n",
    "    points = []\n",
    "    for _ in range(num_points):\n",
    "        x = cx + random.uniform(-num, num)\n",
    "        y = cy + random.uniform(-num, num)\n",
    "        new_point = (x, y)\n",
    "        \n",
    "        # 筛选新点，确保与现有点之间的距离不小于最小距离\n",
    "        if all(math.sqrt((x - px) ** 2 + (y - py) ** 2) >= min_distance for px, py in points):\n",
    "            points.append(new_point)\n",
    "\n",
    "        if len(points) == num:\n",
    "            break\n",
    "\n",
    "    return points\n",
    "\n",
    "# 以圆心坐标 (0, 0) 和半径为1的圆为例，生成10个均匀分布的随机点\n",
    "cx, cy = 0, 0\n",
    "num = 1\n",
    "random_points = generate_random_point(cx, cy, num, num_points=10)\n",
    "for point in random_points:\n",
    "    print(f\"随机点坐标：{point}\")\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "服务器已启动，监听 127.0.0.1:8803\n",
      "待定\n",
      "Deleted file: testModel\\lod1.mtl\n",
      "Deleted file: testModel\\lod1.obj\n",
      "Deleted file: testModel\\lod2.mtl\n",
      "Deleted file: testModel\\lod2.obj\n",
      "Deleted file: testModel\\lod3.mtl\n",
      "Deleted file: testModel\\lod3.obj\n",
      "0\n",
      "0\n",
      "10\n",
      "10\n",
      "10\n",
      "10\n",
      "生成的JSON文件已保存为testData2.json\n",
      "[10]\n",
      "生成的JSON文件已保存为cim_test_city.json\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "127.0.0.1 - - [26/Jan/2024 16:16:59] \"POST /lod/new-test-city HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:16:59] \"GET /testModel/lod1.mtl HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:16:59] \"GET /testModel/lod1.obj?timestamp=1706257019660 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:16:59] \"GET /testModel/lod1.obj?timestamp=1706257019664 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:16:59] \"GET /testModel/lod1.obj?timestamp=1706257019665 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:16:59] \"GET /testModel/lod1.obj?timestamp=1706257019666 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:16:59] \"GET /testModel/lod1.obj?timestamp=1706257019667 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:16:59] \"GET /testModel/lod1.obj?timestamp=1706257019668 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019669 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019670 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019671 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019672 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019673 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019675 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019676 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019677 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019680 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019681 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019683 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:00] \"GET /testModel/lod1.obj?timestamp=1706257019684 HTTP/1.1\" 200 -\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "待定\n",
      "Deleted file: testModel\\lod1.mtl\n",
      "Deleted file: testModel\\lod1.obj\n",
      "0\n",
      "0\n",
      "4\n",
      "4\n",
      "10\n",
      "10\n",
      "0\n",
      "0\n",
      "2\n",
      "2\n",
      "30\n",
      "30\n",
      "生成的JSON文件已保存为testData2.json\n",
      "[30, 10]\n",
      "生成的JSON文件已保存为cim_test_city.json\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "127.0.0.1 - - [26/Jan/2024 16:17:22] \"POST /lod/new-test-city HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:22] \"GET /testModel/lod2.mtl HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:22] \"GET /testModel/lod2.obj?timestamp=1706257042432 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:22] \"GET /testModel/lod2.obj?timestamp=1706257042433 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:22] \"GET /testModel/lod2.obj?timestamp=1706257042434 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:22] \"GET /testModel/lod2.obj?timestamp=1706257042435 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:26] \"GET /testModel/lod1.mtl HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:26] \"GET /testModel/lod1.obj?timestamp=1706257046496 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:26] \"GET /testModel/lod1.obj?timestamp=1706257046497 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:29] \"GET /testModel/lod2.obj?timestamp=1706257049890 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:30] \"GET /testModel/lod1.obj?timestamp=1706257050439 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:35] \"GET /testModel/lod2.obj?timestamp=1706257055520 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:35] \"GET /testModel/lod2.obj?timestamp=1706257055521 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:35] \"GET /testModel/lod2.obj?timestamp=1706257055522 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:36] \"GET /testModel/lod1.obj?timestamp=1706257056285 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:36] \"GET /testModel/lod2.obj?timestamp=1706257056875 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:36] \"GET /testModel/lod2.obj?timestamp=1706257056876 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:37] \"GET /testModel/lod1.obj?timestamp=1706257057389 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:37] \"GET /testModel/lod2.obj?timestamp=1706257057787 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:37] \"GET /testModel/lod2.obj?timestamp=1706257057788 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:38] \"GET /testModel/lod2.obj?timestamp=1706257057789 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:38] \"GET /testModel/lod1.obj?timestamp=1706257058708 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:38] \"GET /testModel/lod2.obj?timestamp=1706257058790 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:38] \"GET /testModel/lod2.obj?timestamp=1706257058791 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:39] \"GET /testModel/lod1.obj?timestamp=1706257059462 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:39] \"GET /testModel/lod2.obj?timestamp=1706257059809 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:17:40] \"GET /testModel/lod1.obj?timestamp=1706257059942 HTTP/1.1\" 200 -\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "待定\n",
      "Deleted file: testModel\\lod1.mtl\n",
      "Deleted file: testModel\\lod1.obj\n",
      "Deleted file: testModel\\lod2.mtl\n",
      "Deleted file: testModel\\lod2.obj\n",
      "0\n",
      "0\n",
      "4\n",
      "4\n",
      "10\n",
      "10\n",
      "0\n",
      "0\n",
      "2\n",
      "2\n",
      "30\n",
      "30\n",
      "0\n",
      "0\n",
      "1\n",
      "1\n",
      "70\n",
      "70\n",
      "生成的JSON文件已保存为testData2.json\n",
      "[70, 30, 10]\n",
      "生成的JSON文件已保存为cim_test_city.json\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "127.0.0.1 - - [26/Jan/2024 16:19:20] \"POST /lod/new-test-city HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:20] \"GET /testModel/lod3.mtl HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:20] \"GET /testModel/lod3.obj?timestamp=1706257160044 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:20] \"GET /testModel/lod3.obj?timestamp=1706257160046 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:20] \"GET /testModel/lod3.obj?timestamp=1706257160047 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:20] \"GET /testModel/lod3.obj?timestamp=1706257160048 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:20] \"GET /testModel/lod3.obj?timestamp=1706257160049 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:20] \"GET /testModel/lod3.obj?timestamp=1706257160050 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:22] \"GET /testModel/lod1.mtl HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:22] \"GET /testModel/lod1.obj?timestamp=1706257162371 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:25] \"GET /testModel/lod2.mtl HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:25] \"GET /testModel/lod2.obj?timestamp=1706257165123 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:25] \"GET /testModel/lod2.obj?timestamp=1706257165126 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:27] \"GET /testModel/lod3.obj?timestamp=1706257167089 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:27] \"GET /testModel/lod3.obj?timestamp=1706257167090 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:44] \"GET /testModel/lod2.obj?timestamp=1706257184865 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:19:47] \"GET /testModel/lod3.obj?timestamp=1706257186986 HTTP/1.1\" 200 -\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "待定\n",
      "Deleted file: testModel\\lod1.mtl\n",
      "Deleted file: testModel\\lod1.obj\n",
      "Deleted file: testModel\\lod2.mtl\n",
      "Deleted file: testModel\\lod2.obj\n",
      "Deleted file: testModel\\lod3.mtl\n",
      "Deleted file: testModel\\lod3.obj\n",
      "0\n",
      "0\n",
      "4\n",
      "4\n",
      "10\n",
      "10\n",
      "0\n",
      "0\n",
      "2\n",
      "2\n",
      "30\n",
      "30\n",
      "0\n",
      "0\n",
      "1\n",
      "1\n",
      "70\n",
      "70\n",
      "生成的JSON文件已保存为testData2.json\n",
      "[70, 30, 10]\n",
      "生成的JSON文件已保存为cim_test_city.json\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "127.0.0.1 - - [26/Jan/2024 16:24:12] \"POST /lod/new-test-city HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:24:12] \"GET /testModel/lod3.mtl HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:24:12] \"GET /testModel/lod3.obj?timestamp=1706257452423 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:24:12] \"GET /testModel/lod3.obj?timestamp=1706257452425 HTTP/1.1\" 200 -\n",
      "127.0.0.1 - - [26/Jan/2024 16:24:12] \"GET /testModel/lod3.obj?timestamp=1706257452426 HTTP/1.1\" 200 -\n"
     ]
    }
   ],
   "source": [
    "import http.server\n",
    "import socketserver\n",
    "import json\n",
    "import cv2\n",
    "import random\n",
    "import math\n",
    "import numpy as np\n",
    "import os\n",
    "from nanoid import generate\n",
    "\n",
    "# 设置服务器的IP地址和端口号\n",
    "server_ip = '127.0.0.1'\n",
    "server_port = 8803\n",
    "\n",
    "# 当底层模型在宽为10，长为10，长宽间隙为10的时候，地图的数据\n",
    "gapDistance = 10\n",
    "\n",
    "\n",
    "class MyHandler(http.server.SimpleHTTPRequestHandler):\n",
    "    def do_GET(self):\n",
    "        if self.path.startswith('/lod/random-city'):\n",
    "\n",
    "            # 生成lod1数据\n",
    "            lod1_data = self.generate_lod_data(1, 0, 0)\n",
    "\n",
    "            # 生成other数据\n",
    "            lod_other_tree = {\n",
    "                \"id\": \"lod1_other\",\n",
    "                \"name\": \"lod1_other\",\n",
    "                \"lod\": \"lod1\",\n",
    "                \"type\": \"other\",\n",
    "                \"children\": [\n",
    "                    {\n",
    "                        \"id\": \"lod2_other\",\n",
    "                        \"name\": \"lod2_other\",\n",
    "                        \"lod\": \"lod2\",\n",
    "                        \"type\": \"other\",\n",
    "                        \"children\": [\n",
    "                            {\n",
    "                                \"id\": \"lod3_other\",\n",
    "                                \"name\": \"lod3_other\",\n",
    "                                \"lod\": \"lod3\",\n",
    "                                \"type\": \"other\",\n",
    "                                \"children\": []\n",
    "                            }\n",
    "\n",
    "                        ]\n",
    "                    }\n",
    "                ]\n",
    "            }\n",
    "\n",
    "            lod0_data = {\n",
    "                \"scene\": {\n",
    "                    \"name\": \"scene1\",\n",
    "                    \"lodsetting\": {\n",
    "                        \"lod1\": {\n",
    "                            \"distance\": 3500,\n",
    "                            \"name\": \"city\"\n",
    "                        },\n",
    "                        \"lod2\": {\n",
    "                            \"distance\": 500,\n",
    "                            \"name\": \"district\"\n",
    "                        },\n",
    "                        \"lod3\": {\n",
    "                            \"distance\": 200,\n",
    "                            \"name\": \"street\"\n",
    "                        },\n",
    "                        \"lod4\": {\n",
    "                            \"distance\": 120,\n",
    "                            \"name\": \"building\"\n",
    "                        },\n",
    "                        \"lod5\": {\n",
    "                            \"distance\": 60,\n",
    "                            \"name\": \"building\"\n",
    "                        }\n",
    "                    },\n",
    "                    \"default_camera\": [\n",
    "                        {\n",
    "                            \"id\": \"01\",\n",
    "                            \"view_direction\": {\n",
    "                                \"x\": 0,\n",
    "                                \"y\": 0,\n",
    "                                \"z\": 0\n",
    "                            },\n",
    "                            \"position\": {\n",
    "                                \"x\": 277,\n",
    "                                \"y\": 449,\n",
    "                                \"z\": 835\n",
    "                            },\n",
    "                            \"pov\": 55,\n",
    "                            \"depth\": 50000\n",
    "                        }\n",
    "                    ],\n",
    "                    \"children\": [],\n",
    "                    \"default_background\": {\n",
    "                        \"color\": \"#c2d2eb\",\n",
    "                        \"colorAlpha\": 1,\n",
    "                        \"ambientLight\": \"0xdddddd\",\n",
    "                        \"sunlight\": {\n",
    "                            \"color\": \"0xdddddd\",\n",
    "                            \"denity\": 0.8,\n",
    "                            \"direction\": {\n",
    "                                \"x\": 1,\n",
    "                                \"y\": 1,\n",
    "                                \"z\": 2\n",
    "                            },\n",
    "                            \"position\": {\n",
    "                                \"x\": 1000,\n",
    "                                \"y\": 1000,\n",
    "                                \"z\": 1000\n",
    "                            }\n",
    "                        },\n",
    "                        \"weather\": \"sunny\",\n",
    "                        \"shadow\": \"true\"\n",
    "                    }\n",
    "                }\n",
    "            }\n",
    "\n",
    "            lod0_data[\"scene\"][\"children\"].append(lod1_data)\n",
    "            lod0_data[\"scene\"][\"children\"].append(lod_other_tree)\n",
    "\n",
    "            # 将lod0数据保存为json文件\n",
    "            with open(\"cim_server.json\", \"w\") as f:\n",
    "                json.dump(lod0_data, f, indent=4)\n",
    "\n",
    "            print(\"生成的JSON文件已保存为cim_server.json\")\n",
    "\n",
    "            responce_data = lod0_data\n",
    "\n",
    "            # 发送响应\n",
    "            self.send_response(200)\n",
    "            self.send_header('Content-type', 'application/json')\n",
    "            self.end_headers()\n",
    "            self.wfile.write(json.dumps(responce_data).encode())\n",
    "        else:\n",
    "            # 默认情况下，使用父类的处理方法\n",
    "            super().do_GET()\n",
    "\n",
    "    def do_POST(self):\n",
    "        if self.path == '/lod/new-test-city':\n",
    "            content_length = int(self.headers['Content-Length'])\n",
    "            body = self.rfile.read(content_length).decode('utf-8')\n",
    "            post_data = json.loads(body)\n",
    "\n",
    "\n",
    "            # 层级规模\n",
    "            LayersNum = post_data.get('midLayersNum')\n",
    "\n",
    "            # 底层规模\n",
    "            rowSize = post_data.get('rowNumber')\n",
    "            columnSize = post_data.get('columnNumber')\n",
    "\n",
    "            # 层级lod实体的规模\n",
    "            num_children = {}\n",
    "            rowcol_children = []\n",
    "            # 检查覆盖的方式是统一还是每层都不一样\n",
    "            if \"layersChildNumber\" in post_data:\n",
    "                # 每层都不同\n",
    "                print('现在不做')\n",
    "            else:\n",
    "                print('待定')\n",
    "                num_children[\"rowChildNumber\"]=int(post_data.get('rowChildNumber')) \n",
    "                num_children[\"columnChildNumber\"]=int(post_data.get('columnChildNumber')) \n",
    "\n",
    "            delete_files_in_folder('testModel')\n",
    "            lod0_data , lodSettingArray= generate_test_lod_data(LayersNum , num_children[\"rowChildNumber\"] ,num_children[\"columnChildNumber\"],int(rowSize),int(columnSize),10,10)\n",
    "            \n",
    "            # 观察距离\n",
    "            # distanceArray = post_data.get('watchDistance')\n",
    "            # for index, dis in enumerate(distanceArray):\n",
    "            #     lod0_data[\"scene\"][\"lodsetting\"][f\"lod{index+1}\"] = {\n",
    "            #         \"distance\": int(dis[\"distance\"]) ,\n",
    "            #         \"name\": f\"lod{index+1}\"\n",
    "            #     }\n",
    "\n",
    "            # distanceMax = post_data.get('watchDistanceMax')\n",
    "            # lod0_data[\"scene\"][\"lodsetting\"][f\"lod{LayersNum+1}\"] = {\n",
    "            #     \"distance\": int(distanceMax),\n",
    "            #     \"name\": f\"lod{LayersNum+1}\"\n",
    "            # }\n",
    "\n",
    "            for index, dis in enumerate(lodSettingArray):\n",
    "                lod0_data[\"scene\"][\"lodsetting\"][f\"lod{index+1}\"] = {\n",
    "                    \"distance\": dis*1.6 ,\n",
    "                    \"name\": f\"lod{index+1}\"\n",
    "                }\n",
    "\n",
    "            \n",
    "\n",
    "            with open('cim_test_city.json', 'w', encoding='utf-8') as f:\n",
    "                f.write(json.dumps(lod0_data, ensure_ascii=False))\n",
    "            print(\"生成的JSON文件已保存为cim_test_city.json\")\n",
    "\n",
    "            \n",
    "            # print(\"lod0_data\")\n",
    "            # print(lod0_data)\n",
    "\n",
    "\n",
    "           # 发送响应\n",
    "            self.send_response(200)\n",
    "            self.send_header('Content-type', 'application/json')\n",
    "            self.end_headers()\n",
    "            self.wfile.write(json.dumps(lod0_data).encode('utf-8'))\n",
    "        else:\n",
    "            # 如果路径不匹配，返回 404 Not Found\n",
    "            self.send_response(404)\n",
    "            self.send_header('Content-type', 'text/html')\n",
    "            self.end_headers()\n",
    "            self.wfile.write(b'Not Found')\n",
    "\n",
    "    def generate_lod_data(self, lod_id, parent_x, parent_z):\n",
    "        lod_prefix = {\n",
    "            1: \"city\",\n",
    "            2: \"distinct\",\n",
    "            3: \"street\",\n",
    "            4: \"building\"\n",
    "        }\n",
    "\n",
    "        max_distance = {2: 2900, 3: 300, 4: 110}\n",
    "\n",
    "        data = {\n",
    "            \"id\": generate(size=8),\n",
    "            \"name\": f\"{lod_prefix[lod_id]}{random.randint(1, 100)}\",\n",
    "            \"lod\": f\"lod{lod_id}\",\n",
    "            \"position\": {\"x\": 0, \"y\": 0, \"z\": 0},\n",
    "            \"rotation\": {\"x\": 0, \"y\": 0, \"z\": 0},\n",
    "            \"model_3d\": [\n",
    "                {\n",
    "                    \"model_url\": f\"lod{lod_id}.obj\",\n",
    "                    \"material_url\": f\"lod{lod_id}.mtl\",\n",
    "                    \"texture_url\": \"\",\n",
    "                    \"shader_url\": \"\"\n",
    "                }\n",
    "            ],\n",
    "            \"children\": []\n",
    "        }\n",
    "\n",
    "        if lod_id > 1:\n",
    "            # 根据距离随机x和z\n",
    "            pos_x, pos_y = self.generate_random_point(\n",
    "                parent_x, parent_z, max_distance[lod_id])\n",
    "            data[\"position\"][\"x\"] = pos_x\n",
    "            data[\"position\"][\"z\"] = pos_y\n",
    "\n",
    "        # 添加子节点\n",
    "        num_children = {1: 20, 2: 5, 3: 20}\n",
    "        child_lod_id = lod_id + 1\n",
    "        if child_lod_id <= 4:\n",
    "\n",
    "            for _ in range(num_children[lod_id]):\n",
    "                child_data = self.generate_lod_data(\n",
    "                    child_lod_id, data[\"position\"][\"x\"], data[\"position\"][\"z\"])\n",
    "                data[\"children\"].append(child_data)\n",
    "\n",
    "        return data\n",
    "\n",
    "    def generate_random_point(self, cx, cy, num):\n",
    "        # 生成在 [-num, num] 范围内的随机坐标\n",
    "        x = cx + random.uniform(-num, num)\n",
    "        y = cy + random.uniform(-num, num)\n",
    "\n",
    "        return x, y\n",
    "\n",
    "\n",
    "        distance = 10\n",
    "\n",
    "        # 计算中心点的坐标\n",
    "        centers = []\n",
    "        for i in range(a):\n",
    "            for j in range(b):\n",
    "                x = (i - (a - 1) / 2) * (block_width + distance) + x_correction\n",
    "                y = (j - (b - 1) / 2) * (block_height + distance) + y_correction\n",
    "                centers.append((x, y))\n",
    "\n",
    "        return centers\n",
    "\n",
    "#调用这个函数来获取测试数据\n",
    "def generate_test_lod_data(layersNumber, contain_x, contain_y, size_x, size_y, block_width, block_height):\n",
    "        # 构建lod\n",
    "        lod0_data = {\n",
    "            \"scene\": {\n",
    "                \"name\": \"scene1\",\n",
    "                \"lodsetting\": {\n",
    "                },\n",
    "                \"default_camera\": [\n",
    "                    {\n",
    "                        \"id\": \"01\",\n",
    "                        \"view_direction\": {\n",
    "                            \"x\": 0,\n",
    "                            \"y\": 0,\n",
    "                            \"z\": 0\n",
    "                        },\n",
    "                        \"position\": {\n",
    "                            \"x\": 277,\n",
    "                            \"y\": 449,\n",
    "                            \"z\": 835\n",
    "                        },\n",
    "                        \"pov\": 55,\n",
    "                        \"depth\": 50000\n",
    "                    }\n",
    "                ],\n",
    "                \"children\": [],\n",
    "                \"default_background\": {\n",
    "                    \"color\": \"#c2d2eb\",\n",
    "                    \"colorAlpha\": 1,\n",
    "                    \"ambientLight\": \"0xdddddd\",\n",
    "                    \"sunlight\": {\n",
    "                        \"color\": \"0xdddddd\",\n",
    "                        \"denity\": 0.8,\n",
    "                        \"direction\": {\n",
    "                            \"x\": 1,\n",
    "                            \"y\": 1,\n",
    "                            \"z\": 2\n",
    "                        },\n",
    "                        \"position\": {\n",
    "                            \"x\": 1000,\n",
    "                            \"y\": 1000,\n",
    "                            \"z\": 1000\n",
    "                        }\n",
    "                    },\n",
    "                    \"weather\": \"sunny\",\n",
    "                    \"shadow\": \"true\"\n",
    "                }\n",
    "            }\n",
    "        }\n",
    "\n",
    "        #暂存数组\n",
    "        global tempArray\n",
    "        #记录层数\n",
    "        layersNumber=layersNumber+1\n",
    "        layersNumberRecord=layersNumber\n",
    "        lodSettingArray=[]\n",
    "\n",
    "        while layersNumber:\n",
    "            #判断是否为最底层\n",
    "            if layersNumber == layersNumberRecord:\n",
    "                move_x=0\n",
    "                move_y=0\n",
    "                size_x=size_x\n",
    "                size_y=size_y\n",
    "                new_width=block_width\n",
    "                new_height=block_height\n",
    "            elif layersNumber < layersNumberRecord:\n",
    "            # 计算x方向平移值\n",
    "                move_x = (contain_x-size_x % contain_x)% contain_x\n",
    "                # x方向数量\n",
    "                size_x = math.ceil(size_x/contain_x)\n",
    "                # 计算y方向平移值\n",
    "                move_y = (contain_y-size_y % contain_y)% contain_y\n",
    "                # y方向数量\n",
    "                size_y = math.ceil(size_y/contain_y)\n",
    "                # 计算x方向长度\n",
    "                new_width = contain_x*block_width+gapDistance*(contain_x-1)\n",
    "                # 计算y方向长度\n",
    "                new_height = contain_y*block_height+gapDistance*(contain_y-1)\n",
    "\n",
    "            #创建对应的模型文件\n",
    "            json_data_example = {\n",
    "                \"buildingArray\": [\n",
    "                    {\n",
    "                        \"name\": f\"lod{layersNumber}\",\n",
    "                        \"points\": [\n",
    "                            {\"lon\": -new_width/2, \"lat\": -new_height/2, \"height\": (layersNumberRecord-layersNumber+1)*10},\n",
    "                            {\"lon\": -new_width/2, \"lat\": new_height/2, \"height\": (layersNumberRecord-layersNumber+1)*10},\n",
    "                            {\"lon\": new_width/2, \"lat\": new_height/2, \"height\": (layersNumberRecord-layersNumber+1)*10},\n",
    "                            {\"lon\": new_width/2, \"lat\": -new_height/2, \"height\": (layersNumberRecord-layersNumber+1)*10}\n",
    "                        ],\n",
    "                        \"pointsOrder\": \"order\",\n",
    "                        \"center\": {\"lon\": 0, \"lat\": 0}\n",
    "                    }\n",
    "                ]\n",
    "            }\n",
    "\n",
    "            read_data(json_data_example)\n",
    "            lodSettingArray.insert(0, max(new_width, new_height))\n",
    "\n",
    "\n",
    "            #创建二维数组\n",
    "            two_d_array = np.zeros((size_x, size_y), dtype=object)\n",
    "            # 计算中心点\n",
    "            print(move_x)\n",
    "            print(move_y)\n",
    "            print(size_x)\n",
    "            print(size_y)\n",
    "            print(new_width)\n",
    "            print(new_height)\n",
    "            centers,matrix_positions= calculate_block_centers(size_x, size_y, new_width, new_height, move_x*(block_width+gapDistance)/2, move_y*(block_height+gapDistance)/2,True)\n",
    "            #添加至lod0的孩子\n",
    "            for center, matrix_position in zip(centers, matrix_positions):\n",
    "                center_x, center_y = center\n",
    "                matrix_x, matrix_y = matrix_position\n",
    "                data = {\n",
    "                    \"id\": generate(size=8),\n",
    "                    \"name\": f\"lod{layersNumber}\",\n",
    "                    \"lod\": f\"lod{layersNumber}\",\n",
    "                    \"position\": {\"x\": center_x, \"y\": 0, \"z\": center_y},\n",
    "                    \"rotation\": {\"x\": 0, \"y\": 0, \"z\": 0},\n",
    "                    \"model_3d\": [\n",
    "                        {\n",
    "                            \"model_url\": f\"lod{layersNumber}.obj\",\n",
    "                            \"material_url\": f\"lod{layersNumber}.mtl\",\n",
    "                            \"texture_url\": \"\",\n",
    "                            \"shader_url\": \"\"\n",
    "                        }\n",
    "                    ],\n",
    "                    \"children\": []\n",
    "                }\n",
    "                #检查是否为最底层，最底层无需添加孩子\n",
    "                if layersNumber < layersNumberRecord :\n",
    "                    #获取子层级的左下角初始点\n",
    "                    first_child_x=matrix_x*contain_x\n",
    "                    first_child_y=matrix_y*contain_y\n",
    "\n",
    "                    #从初始点开始放入children\n",
    "                    for i in range(contain_x):\n",
    "                        for j in range(contain_y):\n",
    "                            try:\n",
    "                                data[\"children\"].append(tempArray[first_child_x+i, first_child_y+j])\n",
    "                            except IndexError as e:\n",
    "                                print(f\"发生错误: {e}\")\n",
    "                            \n",
    "\n",
    "                two_d_array[matrix_x,matrix_y]=data\n",
    "            \n",
    "            block_width=new_width\n",
    "            block_height=new_height\n",
    "\n",
    "\n",
    "            layersNumber = layersNumber-1\n",
    "            tempArray=two_d_array\n",
    "            # print('矩阵打印')\n",
    "            # print(two_d_array)\n",
    "        \n",
    "        #把结果存入lod0_data\n",
    "        lod0_data[\"scene\"][\"children\"]=json.loads(json.dumps(tempArray.flatten().tolist()) )\n",
    "        # 将lod0数据保存为json文件\n",
    "        with open(\"testData2.json\", \"w\") as f:\n",
    "            json.dump(lod0_data, f, indent=4)\n",
    "        print(\"生成的JSON文件已保存为testData2.json\")\n",
    "        print(lodSettingArray)\n",
    "        return lod0_data,lodSettingArray\n",
    "        \n",
    "def calculate_block_centers(a, b, block_width, block_height, x_correction=0, y_correction=0, matrix_coordinates=False):\n",
    "    distance = 10\n",
    "\n",
    "    # 计算中心点的坐标和方阵位置\n",
    "    centers = []\n",
    "    matrix_positions = []\n",
    "    for i in range(a):\n",
    "        for j in range(b):\n",
    "            x = (i - (a - 1) / 2) * (block_width + distance) + x_correction\n",
    "            y = (j - (b - 1) / 2) * (block_height + distance) + y_correction\n",
    "            matrix_positions.append((i, j))  # 使用阵列坐标表示方块位置\n",
    "            centers.append((x, y))  # 使用常规坐标表示方块中心点\n",
    "\n",
    "    return centers, matrix_positions\n",
    "\n",
    "def point_cross_triangle_border(p0, p1, p2):\n",
    "    # 判断p0为起始点，y轴向下方向的射线，是否与p1，p2线段相交\n",
    "    cross_point_count = 0\n",
    "\n",
    "    if p1['lon'] < p2['lon']:\n",
    "        min_lon, max_lon = p1['lon'], p2['lon']\n",
    "    else:\n",
    "        min_lon, max_lon = p2['lon'], p1['lon']\n",
    "\n",
    "    if p0['lon'] >= min_lon and p0['lon'] <= max_lon:\n",
    "        # p0的x在p1和p2之间才有意义\n",
    "        # 判断p1，p2的x是否一样\n",
    "        if p1['lon'] == p2['lon']:\n",
    "            # p1p2是一条垂直线\n",
    "            # 不用检查p0是否在p1p2上\n",
    "            cross_point_count += 0\n",
    "        else:\n",
    "            # p1p2不是垂直线，找到p1p2的方程式\n",
    "            k = (p2['lat'] - p1['lat']) / (p2['lon'] - p1['lon'])\n",
    "            b = p2['lat'] - k * p2['lon']\n",
    "            crosspoint_y = p0['lon'] * k + b\n",
    "            if p0['lat'] > crosspoint_y:\n",
    "                cross_point_count += 1\n",
    "\n",
    "    return cross_point_count\n",
    "\n",
    "\n",
    "def check_point(i, p2, points):\n",
    "    p1 = None\n",
    "    p3 = None\n",
    "    length = len(points)\n",
    "    \n",
    "    if i == 1:\n",
    "        p1 = points[length - 1]\n",
    "        p3 = points[i]\n",
    "    elif i == length:\n",
    "        p1 = points[i - 2]\n",
    "        p3 = points[0]\n",
    "    else:\n",
    "        p1 = points[i - 2]\n",
    "        p3 = points[i]\n",
    "\n",
    "    v1 = {\n",
    "        'x': p2['lon'] - p1['lon'],\n",
    "        'y': p2['lat'] - p1['lat'],\n",
    "        'z': 0\n",
    "    }\n",
    "    v2 = {\n",
    "        'x': p3['lon'] - p2['lon'],\n",
    "        'y': p3['lat'] - p2['lat'],\n",
    "        'z': 0\n",
    "    }\n",
    "\n",
    "    z = v1['x'] * v2['y'] - v1['y'] * v2['x']\n",
    "\n",
    "    if z < 0:\n",
    "        return True\n",
    "    elif z == 0:\n",
    "        return True\n",
    "    else:\n",
    "        return False\n",
    "\n",
    "def is_point_inside_triangle(p0, p1, p2, p3):\n",
    "    def sign(p1, p2, p3):\n",
    "        return (p1[\"x\"] - p3[\"x\"]) * (p2[\"y\"] - p3[\"y\"]) - (p2[\"x\"] - p3[\"x\"]) * (p1[\"y\"] - p3[\"y\"])\n",
    "\n",
    "    d1 = sign(p0, p1, p2)\n",
    "    d2 = sign(p0, p2, p3)\n",
    "    d3 = sign(p0, p3, p1)\n",
    "\n",
    "    has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0)\n",
    "    has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0)\n",
    "\n",
    "    return not (has_neg and has_pos)\n",
    "\n",
    "def cut_polygon(points):\n",
    "    # 会多次调用的图形切割方法\n",
    "    # points为导入的点\n",
    "    # 先判断这个图形是不是凸多边形\n",
    "    is_convex = True\n",
    "    convex_points = []  # 凸点数组\n",
    "    for i in range(1, len(points) + 1):\n",
    "        p2 = points[i - 1]\n",
    "\n",
    "        if not check_point(i, p2, points):\n",
    "            is_convex = False\n",
    "        else:\n",
    "            convex_points.append(i)\n",
    "\n",
    "    result = {\n",
    "        'convex': [],\n",
    "        'triangles': []\n",
    "    }\n",
    "\n",
    "    if is_convex:\n",
    "        # 是凸多边形，返回自身的点\n",
    "        result['convex'] = points\n",
    "        # print(\"是凸多边形\")\n",
    "        return result\n",
    "    else:\n",
    "        # -----------------------\n",
    "        # 是凹多边形，要递归切割\n",
    "        # print(\"是凹多边形\")\n",
    "        # -----------------------\n",
    "        # 遍历凸点，找到一个能切割的点\n",
    "        for point in convex_points:\n",
    "            p1 = None\n",
    "            p2 = None\n",
    "            p3 = None\n",
    "            length = len(points)\n",
    "            p1_pos = -1  # 当前凸点及其两旁的点，在图形中的位置\n",
    "            p2_pos = -1\n",
    "            p3_pos = -1\n",
    "            if point == 1:\n",
    "                p1 = points[length - 1]\n",
    "                p2 = points[0]\n",
    "                p3 = points[1]\n",
    "                p1_pos = length - 1\n",
    "                p2_pos = 0\n",
    "                p3_pos = 1\n",
    "            elif point == length:\n",
    "                p1 = points[length - 2]\n",
    "                p2 = points[length - 1]\n",
    "                p3 = points[0]\n",
    "                p1_pos = length - 2\n",
    "                p2_pos = length - 1\n",
    "                p3_pos = 0\n",
    "            else:\n",
    "                p1 = points[point - 2]\n",
    "                p2 = points[point - 1]\n",
    "                p3 = points[point]\n",
    "                p1_pos = point - 2\n",
    "                p2_pos = point - 1\n",
    "                p3_pos = point\n",
    "\n",
    "            # 遍历points中除去p1、p2、p3的点，是否在p1p2p3的三角形内\n",
    "            conflict = False\n",
    "            for j in range(length):\n",
    "                if j != p1_pos and j != p2_pos and j != p3_pos:\n",
    "                    # 遍历到非p1p2p3的点的时候\n",
    "                    # 把这个点从points中拿出来，检测在不在p1p2p3的三角形内\n",
    "                    if is_point_inside_triangle(points[j], p1, p2, p3):\n",
    "                        conflict = True\n",
    "\n",
    "            # 遍历结束，查看冲突值，如果为true，则发生冲突，该点不能切割\n",
    "            if conflict:\n",
    "                print(p2, \" 不是可划分点\")\n",
    "            else:\n",
    "                print(p2, \" 是可划分点\")\n",
    "                # 开始划分\n",
    "                # 切割三角形出来\n",
    "                tri_list = {\n",
    "                    'p1': p1,\n",
    "                    'p2': p2,\n",
    "                    'p3': p3\n",
    "                }\n",
    "                # 切割新的多边形出来\n",
    "                new_points = points.copy()\n",
    "                new_points.pop(p2_pos)\n",
    "                result2 = cut_polygon(new_points)\n",
    "                result['convex'] = result2['convex']\n",
    "                result['triangles'] = result2['triangles'].copy()\n",
    "                result['triangles'].append(tri_list)\n",
    "                # 停止循环\n",
    "                break\n",
    "\n",
    "        return result\n",
    "\n",
    "def find_pos(substring, vcontent):\n",
    "    # 找到点的原地点的部分\n",
    "    v_arr = vcontent.split('\\nv ')\n",
    "    for i, v_item in enumerate(v_arr):\n",
    "        if substring == v_item:\n",
    "            return i\n",
    "    return -1\n",
    "\n",
    "def read_data(json_data):\n",
    "    for info in json_data['buildingArray']:\n",
    "        # 创建obj文件\n",
    "        title_content = f\"mtllib {info['name']}.mtl\\no {info['name']}\"\n",
    "        points = info['points']\n",
    "        points_length = len(points)\n",
    "\n",
    "        if info['pointsOrder'] == \"reverse\":\n",
    "            # 逆时针排序，改为顺时针\n",
    "            points.reverse()\n",
    "\n",
    "        # v点\n",
    "        v_content = \"\"\n",
    "        # 获取基准点\n",
    "        basic_point = info['center'] if 'center' in info else {'lon': points[0]['lon'], 'lat': points[0]['lat']}\n",
    "        for point in points:\n",
    "            new_lon = point['lon'] - basic_point['lon']\n",
    "            new_lat = point['lat'] - basic_point['lat']\n",
    "            v_content += f\"\\nv {new_lon} {point['height']} {new_lat}\"\n",
    "        for point in points:\n",
    "            new_lon = point['lon'] - basic_point['lon']\n",
    "            new_lat = point['lat'] - basic_point['lat']\n",
    "            point['lon'] = new_lon\n",
    "            point['lat'] = new_lat\n",
    "            v_content += f\"\\nv {new_lon} 0 {new_lat}\"\n",
    "\n",
    "        # 贴图\n",
    "        vt_content = \"\\nvt 0.625000 0.500000\"\n",
    "        # 法向量\n",
    "        vn_content = \"\\nvn 0 1 0\\nvn 0 -1 0\"\n",
    "        # 侧面\n",
    "        for i in range(1, points_length + 1):\n",
    "            point1 = points[i - 1]\n",
    "            point2 = points[i - 1]\n",
    "            point3 = points[i] if i < points_length else points[0]\n",
    "            a = (0 - point1['height']) * (point3['lat'] - point1['lat']) - (0 - point1['height']) * (\n",
    "                    point2['lat'] - point1['lat'])\n",
    "            b = 0\n",
    "            c = (point2['lon'] - point1['lon']) * (0 - point1['height']) - (\n",
    "                    point3['lon'] - point1['lon']) * (0 - point1['height'])\n",
    "            vn_content += f\"\\nvn {a} {b} {c}\"\n",
    "\n",
    "        # 生成面组\n",
    "        face_content = \"\\ng box_Cube\\nusemtl Material01\\ns off\"\n",
    "        # 生成侧面\n",
    "        side_face_content = \"\"\n",
    "        for i in range(1, points_length + 1):\n",
    "            side_face_content += \"\\nf \"\n",
    "            if i < points_length:\n",
    "                side_face_content += f\"{i}/1/{i + 2} {i + points_length}/1/{i + 2} {i + points_length + 1}/1/{i + 2} {i + 1}/1/{i + 2}\"\n",
    "            else:\n",
    "                side_face_content += f\"{i}/1/{i + 2} {i + points_length}/1/{i + 2} {1 + points_length}/1/{i + 2} 1/1/{i + 2}\"\n",
    "\n",
    "        # 生成顶面和底面（new！！）\n",
    "        # 顶面\n",
    "        top_face_content = \"\"\n",
    "        # 底面\n",
    "        bottom_face_content = \"\"\n",
    "\n",
    "        # 获取切割结果\n",
    "        cut_face = cut_polygon(points)\n",
    "        # print(cut_face)\n",
    "        # 这里还有点问题，要改\n",
    "        # 已经处理了\\nf 出现的问题\n",
    "        # 生成切割后的凸多边形\n",
    "        success_convex = False\n",
    "        convex = cut_face['convex']\n",
    "        for point in convex:\n",
    "            substring = f\"{point['lon']} {point['height']} {point['lat']}\"\n",
    "            num = find_pos(substring, v_content)\n",
    "            if num != -1:\n",
    "                top_face_content += f\"{num}/1/1 \"\n",
    "                bottom_face_content = f\"{num + points_length}/1/2 {bottom_face_content}\"\n",
    "                success_convex = True\n",
    "            else:\n",
    "                print(\"构造凸多边形的时候，出现了不存在的点\")\n",
    "\n",
    "        if success_convex:\n",
    "            top_face_content = f\"\\nf {top_face_content}\"\n",
    "            bottom_face_content = f\"\\nf {bottom_face_content}\"\n",
    "\n",
    "        # 生成切割后的多个三角形\n",
    "        triangles = cut_face['triangles']\n",
    "        for tri in triangles:\n",
    "            top_face_content += \"\\nf \"\n",
    "            for point in tri.values():\n",
    "                substring = f\"{point['lon']} {point['height']} {point['lat']}\"\n",
    "                num = find_pos(substring, v_content)\n",
    "                if num != -1:\n",
    "                    top_face_content += f\"{num}/1/1 \"\n",
    "                    bottom_face_content = f\"{num + points_length}/1/2 {bottom_face_content}\"\n",
    "                else:\n",
    "                    print(\"构造三角形的时候，出现了不存在的点\")\n",
    "            bottom_face_content = f\"\\nf {bottom_face_content}\"\n",
    "\n",
    "        # 这里top_face_content和bottom_face_content添加\"\\n f\"的方式不同是因为顶面和底面的点顺序不同\n",
    "        # print('top_face_content',top_face_content);\n",
    "        # print('bottom_face_content',bottom_face_content);\n",
    "        # print('side_face_content',bottom_face_content)\n",
    "\n",
    "        # obj文件统合\n",
    "        obj_content = f\"{title_content}{v_content}{vt_content}{vn_content}{face_content}{top_face_content}{bottom_face_content}{side_face_content}\"\n",
    "        # 创建mtl文件\n",
    "        mtl_content = \"newmtl Material01\\nNs 50\\nKa 1 1 1\\nKd 0.800000 0.269435 0.285941\\nKs 0.664835 0.664835 0.664835\\nKe 0 0 0\\nNi 1.450000\\nd 1.000000\\nillum 2\"\n",
    "\n",
    "        # Python的文件生成方式\n",
    "        folder_name = \"testModel\"\n",
    "        # 确保文件夹存在，如果不存在则创建\n",
    "        if not os.path.exists(folder_name):\n",
    "            os.makedirs(folder_name)\n",
    "\n",
    "        # 保存 obj 文件到 testModel 文件夹下\n",
    "        obj_file_path = os.path.join(folder_name, f\"{info['name']}.obj\")\n",
    "        with open(obj_file_path, \"w\") as obj_file:\n",
    "            obj_file.write(obj_content)\n",
    "        mtl_file_path = os.path.join(folder_name, f\"{info['name']}.mtl\")\n",
    "        with open(mtl_file_path, \"w\") as mtl_file:\n",
    "            mtl_file.write(mtl_content)\n",
    "\n",
    "def delete_files_in_folder(folder_path):\n",
    "    # 获取文件夹下所有文件\n",
    "    file_list = os.listdir(folder_path)\n",
    "\n",
    "    # 遍历文件夹下的所有文件\n",
    "    for file_name in file_list:\n",
    "        file_path = os.path.join(folder_path, file_name)\n",
    "\n",
    "        # 判断是否是文件，并且是 obj 或 mtl 文件\n",
    "        if os.path.isfile(file_path) and (file_name.endswith('.obj') or file_name.endswith('.mtl')):\n",
    "            # 删除文件\n",
    "            os.remove(file_path)\n",
    "            print(f\"Deleted file: {file_path}\")\n",
    "\n",
    "\n",
    "\n",
    "# 创建多线程服务器并监听指定IP地址和端口号1\n",
    "class ThreadedHTTPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):\n",
    "    pass\n",
    "\n",
    "\n",
    "with ThreadedHTTPServer((server_ip, server_port), MyHandler) as httpd:\n",
    "    print(f\"服务器已启动，监听 {server_ip}:{server_port}\")\n",
    "    try:\n",
    "        httpd.serve_forever()\n",
    "    except KeyboardInterrupt:\n",
    "        pass\n",
    "    httpd.server_close()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Block Centers:\n",
      "Center: (-20.0, -20.0), Matrix Position: (0, 0)\n",
      "Center: (-20.0, 20.0), Matrix Position: (0, 1)\n",
      "Center: (20.0, -20.0), Matrix Position: (1, 0)\n",
      "Center: (20.0, 20.0), Matrix Position: (1, 1)\n"
     ]
    },
    {
     "data": {
      "image/png": "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2LCBwsJCotEora2tVFVVMTw8TE9Pz/55FhcXE4lEaGtrIxKJ0N/fTzwe319eUlJCV1cXTU1NVFdXE4/H6evr219eVlZGeXk5HR0dRKNRtm7dysDAwP7y8vJySktLicVih7RMiS4AXV3pL9OWLVuYP3/+uMtUWVlJe3t76Ms0NDTEI488kpX1lFim+fOrKS+P09SU/WXq6emht7c3K+upuLiYiooItbVtxGLZX0/d3d3MmTNnUuteJutp9DK1trYyZcqUnNqehoaGGBgYYNq0aQwNDVFUVMTIyAiDg4NMnz6d3bt3U1BQwNSpU9mzZw/Tpk1j7969jIyMMGPGDHbv3k1hYSFTpkzZX/7iiy+yb9++A8oLCwspLR1mcHA6AwN7UNX95VOmTEFEePHFF5k+fTp79njlxcXFDA4O5sxzWHORnOiSISJlwM+AjwG/V9WypLLtqjrudcVc7JIRxO3XQWLJNwjXILs5rFq1imuuuSZr8wvS1VI9gNz0HatLxsjISNbvQB2rS0ZhYSH19fWoKoWFhXzzm9/kjDPOIBaL8Za3vIXW1lbgwC4ZE3HcccfVr1+/vm3evHl7x5pmx44dBZdccsn8Rx55pHTatGlaVla299prr+0888wzU7xhcny+973vldXW1g41NjZm/ekX43XJyInO+6raBzQBy4BuEZkH4P/tCc/s0GlutvOeN7Dla8kVbL1P0VpsLfnu3j1571MsLi6mpaWFJ554gmuuuYZVq1ZNyu+uWLEiMnv27L2xWKx18+bNG++4445/9PT0HFKL5M9//vOyJ598MqNbt7PRRB3m3afH+GeIiEgxcBbwNHAPcKE/2YXA3aEIHiaWbq4AW76WXMF1yQgSS75jniWuXQuRCBQUeH/Xrs3q7+7cuTPlQ+mHhob4zGc+UxSNRmtrampqf/GLX5SC94zWlStXVkWj0dpoNFq7evXqAy5hDQwMyGte85pFX//61+ckj9+4ceO0xx9/fOY3vvGNLYllra2tHT7//PN3ANx0001H19fX1yxZsqT2ve9974K9e70TzhkzZpzysY997LjFixfXnnzyyUuee+65KevWrZt5//33l332s5+tWrJkSe3GjRunbdy4cdprXvOaRSeeeGJNY2Pj4sQrr84999zIRRddVPXKV74yeumll1b96le/KlmyZEntkiVLamtqamq3b9+eUZ4L85riPOB2/7piAXCXqv5SRB4F7hKRDwPPAueF6HjI5FqTzkRY8rXkCrbuOrQWW0u+KZPi2rWwciUkziKfecYbBlix4pB/a3BwkIaGBoaGhujq6uLBBx88aJobb7wRgPb29qcef/zx6W9+85sXdXR0tK5Zs2bOM888M23jxo1PTZ06le7u7v3iO3fuLDj33HNf9t73vjd+2WWXHfBOtJaWlum1tbW7U/XH/Otf/zr9xz/+8dHr169/etq0afq+973v+G9961vll112WXxwcLDg9NNPH1izZs2Wiy++uGrNmjXHXHvttV1nnXVW31ve8pYdH/zgB7cDnH766dGbb775mfr6+j0PPvjgzEsuueT4P/3pT+0AHR0d0//whz+0T5kyhTPPPPOEG2644Zk3vvGNu3bs2FEwY8aMfZnELrSkqKpPAqekGB8HXj/5RtklFosRiUTC1kgbS76WXMHWQ6utxdaS7/Dw8MEHSFdd9VJCTLB7tzf+MJJiovkU4NFHH+X973///uuICX7/+9/z1re+dS/AKaecMlRZWTm8YcOG6Q8++OCsiy+++PnEzThz587d/yTzt73tbSdcccUV2y655JIXMvG59957S1tbW2ecfPLJNQBDQ0MFFRUVewGmTp2qibPJxsbGXffff/+s0d/fsWNHweOPP15y3nnn7X9X2PDw8P5XcbzjHe/YnkjGr3rVqwauvPLK+e9617teeM973rO9urraRlI80kl1oT2XseRryRVgzpw5E0+UI1iLrSXflI/7e/bZ1BOPNf4QOP300+nt7eX5558/YPxYN1n6r35KWfiKV7xi4N577z3qIx/5yAsFBQe2SjY0NAy1tbXNSHVDkarKeeedF7/xxhu3jJ7nlClTNDGvKVOmsHfv3oPeOzUyMkJpaenep59+OuUTd0pKSvYnvq985SvbzjnnnB133333UWeccUbNvffe237KKaekfbNOTtxocyQSi8XCVsgIS76WXMHemaIlLPmmfKLN8cennnis8YfA008/zcjIyEHXX1/72tfyq1/9agrAk08+Oa2rq6vopJNOGjrrrLN2futb3zomcdNKcvPpddddt/Xoo4/ee8EFFxwkeOKJJ+456aSTdn3yk5+s3LfPy1EbNmyYduedd5YtW7Zs5y9/+cvZiVdOdXd3F7a3t4/7csmSkpKRnTt3FgAcffTR+6qqqoZv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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def calculate_block_centers(a, b, block_width, block_height, x_correction=0, y_correction=0, matrix_coordinates=False):\n",
    "    distance = 10\n",
    "\n",
    "    # 计算中心点的坐标和方阵位置\n",
    "    centers = []\n",
    "    matrix_positions = []\n",
    "    for i in range(a):\n",
    "        for j in range(b):\n",
    "            x = (i - (a - 1) / 2) * (block_width + distance) + x_correction\n",
    "            y = (j - (b - 1) / 2) * (block_height + distance) + y_correction\n",
    "            matrix_positions.append((i, j))  # 使用阵列坐标表示方块位置\n",
    "            centers.append((x, y))  # 使用常规坐标表示方块中心点\n",
    "\n",
    "    return centers, matrix_positions\n",
    "\n",
    "# 例如，横向有3个方块，纵向有2个方块，方块的长为15，宽为10，修正值为(5, 5)，使用阵列坐标\n",
    "a = 2\n",
    "b = 2\n",
    "block_width = 30\n",
    "block_height = 30\n",
    "x_correction = 0\n",
    "y_correction = 0\n",
    "\n",
    "centers, matrix_positions = calculate_block_centers(a, b, block_width, block_height, x_correction, y_correction, matrix_coordinates=True)\n",
    "\n",
    "# 打印方块中心点坐标和阵列坐标\n",
    "print(\"Block Centers:\")\n",
    "for center, matrix_position in zip(centers, matrix_positions):\n",
    "    print(f\"Center: {center}, Matrix Position: {matrix_position}\")\n",
    "\n",
    "# 绘制方块中心点\n",
    "x_centers, y_centers = zip(*centers)\n",
    "plt.scatter(x_centers, y_centers, marker='o', color='red', label='Block Centers')\n",
    "\n",
    "# 绘制方块\n",
    "for center in centers:\n",
    "    x, y = center\n",
    "    plt.Rectangle((x - block_width / 2, y - block_height / 2), block_width, block_height, fill=None, edgecolor='blue', linewidth=2)\n",
    "    plt.gca().add_patch(plt.Rectangle((x - block_width / 2, y - block_height / 2), block_width, block_height, fill=None, edgecolor='blue', linewidth=2))\n",
    "\n",
    "plt.axhline(0, color='black', linewidth=0.5)\n",
    "plt.axvline(0, color='black', linewidth=0.5)\n",
    "plt.grid(color='gray', linestyle='--', linewidth=0.5)\n",
    "\n",
    "plt.xlabel('X-axis')\n",
    "plt.ylabel('Y-axis')\n",
    "plt.title(f'Block Arrangement ({a}x{b}) with Block Size ({block_width}x{block_height}), Corrections ({x_correction},{y_correction})')\n",
    "\n",
    "# 调整图例大小\n",
    "plt.legend(loc='upper right', bbox_to_anchor=(1.2, 1), title='Legend')\n",
    "\n",
    "plt.show()\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.8 ('base')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "e42634819b8c191a5d07eaf23810ff32516dd8d3875f28ec3e488928fbd3c187"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
